package com.dingxin.ship_together_api.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dingxin.ship_together_api.common.utils.PageUtils;
import com.dingxin.ship_together_api.common.utils.SaUtils;
import com.dingxin.ship_together_api.config.exception.CustomException;
import com.dingxin.ship_together_api.entity.*;
import com.dingxin.ship_together_api.entity.enums.ExceptionEnum;
import com.dingxin.ship_together_api.entity.vo.PageQueryVo;
import com.dingxin.ship_together_api.entity.vo.route.RouteSkuQueryVo;
import com.dingxin.ship_together_api.entity.vo.route.RouteSpuQueryVo;
import com.dingxin.ship_together_api.mapper.*;
import com.dingxin.ship_together_api.service.RouteSpuService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
* Author: ctj
* Date: 2022-09-30
* Time: 17:15
*/

@Service
public class RouteSpuServiceImpl extends ServiceImpl<RouteSpuMapper, RouteSpu> implements RouteSpuService {

    @Autowired
    private RouteSpuMapper routeSpuMapper;
    @Autowired
    private ShipInfoMapper shipInfoMapper;
    @Autowired
    private RouteSkuMapper routeSkuMapper;
    @Autowired
    private RouteDestinationMapper destinationMapper;
    @Autowired
    private RouteTimeMapper timeMapper;
    @Autowired
    private RouteSeatMapper seatMapper;
    @Autowired
    private SaUtils saUtils;
    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    @Override public PageUtils selectSpuPage(PageQueryVo pageQueryVo) {
        // 设置分页大小
        Page<RouteSpu> page = new Page<>(pageQueryVo.getCurrPage(),pageQueryVo.getPageSize());
        // 构造分页查询条件
        QueryWrapper<RouteSpu> queryWrapper = new QueryWrapper<>();
        // 筛选用户角色，详见方法注释
        saUtils.generateQueryWrapper(queryWrapper, pageQueryVo);
        // 如果startTime不为空，则根据日期倒查当天有航线的spu列表
        if (pageQueryVo.getStartDate() != null) {
            genQWByStartTime(queryWrapper, pageQueryVo.getStartDate());
        }
        // 如果sku库存不足，则不显示该spu
        List<RouteSku> skuList = routeSkuMapper.selectList(
            new QueryWrapper<RouteSku>().gt(RouteSku.COL_STOCK, 0).groupBy(RouteSku.COL_ROUTE_SPU_ID));
        queryWrapper.in(RouteSpu.COL_ID,skuList.stream().map(RouteSku::getRouteSpuId).collect(Collectors.toList()));
        // 分页查询
        Page<RouteSpu> resPage = routeSpuMapper.selectPage(page, queryWrapper);
        PageUtils pageUtils = new PageUtils(resPage);
        pageUtils.setList(routeSpuToRouteSpuQueryVo(resPage.getRecords()));
        // 结果封装
        return pageUtils;
    }

    /**
     * 根据startTime倒查询spu信息QW
     * @param spuQueryWrapper
     * @param startDate
     * @return
     */
    private QueryWrapper<RouteSpu> genQWByStartTime(QueryWrapper<RouteSpu> spuQueryWrapper, Date startDate) {
        // 根据startTime查询所有的航线时间
        QueryWrapper<RouteTime> timeQueryWrapper = new QueryWrapper<>();
        timeQueryWrapper.between(RouteTime.COL_START_TIME, startDate, DateUtil.offsetDay(startDate, 1));
        List<RouteTime> routeTimes = timeMapper.selectList(timeQueryWrapper);
        if (routeTimes.size()<1) {
            throw new CustomException(ExceptionEnum.ROUTE_NOT_FOUND_IN_DATE);
        }
        // 根据航线信息查询所有的spu信息
        Set<Long> routeTimeIds = routeTimes.stream().map(RouteTime::getId).collect(Collectors.toSet());
        QueryWrapper<RouteSku> skuWrapper = new QueryWrapper<>();
        skuWrapper.in(RouteSku.COL_TIME_ID, routeTimeIds);
        List<RouteSku> skuList = routeSkuMapper.selectList(skuWrapper);
        if (skuList.size()==0) {
            // 如果没有sku信息，返回空列表，不报错，增强前端体验
            spuQueryWrapper.eq(RouteSpu.COL_ID,-1);
            return spuQueryWrapper;
        }
        spuQueryWrapper.in(RouteSpu.COL_ID, skuList.stream().map(RouteSku::getRouteSpuId).collect(Collectors.toList()));
        return spuQueryWrapper;
    }

    /**
     * routeSpu 转 routeSpuQueryVo
     * 主要工作是去除routeSpu不需要的字段+将shipId转换ShipInfo注入到routeSpuQueryVo
     * @param routeSpuList
     */
    private List<RouteSpuQueryVo> routeSpuToRouteSpuQueryVo(List<RouteSpu> routeSpuList) {
        if (routeSpuList.size()==0) {
            return new ArrayList<>();
        }
        // 准备需要的数据
        List<Long> shipIdList = routeSpuList.stream().map(RouteSpu::getShipId).collect(Collectors.toList());
        Map<Long, ShipInfo> shipInfoMap = shipInfoMapper.selectList(new QueryWrapper<ShipInfo>().in(ShipInfo.COL_ID, shipIdList))
                                                        .stream().collect(Collectors.toMap(ShipInfo::getId, shipInfo -> shipInfo));
        // 组装数据
        List<RouteSpuQueryVo> resultList = new ArrayList<>();
        routeSpuList.forEach(routeSpu -> {
            RouteSpuQueryVo routeSpuQueryVo = new RouteSpuQueryVo();
            routeSpuQueryVo.setShipInfo(shipInfoMap.get(routeSpu.getShipId()));
            BeanUtils.copyProperties(routeSpu,routeSpuQueryVo);
            resultList.add(routeSpuQueryVo);
        });
        return resultList;
    }

    @Override public PageUtils selectSkuPageBySpuId(PageQueryVo pageQueryVo) {
        if (StringUtils.isEmpty(pageQueryVo.getSpuId().toString())) {
            throw new CustomException(ExceptionEnum.MISSING_PARAMETER);
        }
        // 设置分页大小
        Page<RouteSku> page = new Page<>(pageQueryVo.getCurrPage(),pageQueryVo.getPageSize());
        // 构造分页查询条件
        QueryWrapper<RouteSku> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(RouteSku.COL_ROUTE_SPU_ID,pageQueryVo.getSpuId());
        Page<RouteSku> routeSkuPage = routeSkuMapper.selectPage(page, saUtils.generateQueryWrapper(queryWrapper,pageQueryVo));
        PageUtils pageUtils = new PageUtils(routeSkuPage);
        pageUtils.setList(routeSkuToRouteSkuQueryVo(routeSkuPage.getRecords()));
        return pageUtils;
    }

    /**
     * routeSku 转 routeSkuQueryVo
     * @param routeSkuList
     * @return
     */
    private List<RouteSkuQueryVo> routeSkuToRouteSkuQueryVo(List<RouteSku> routeSkuList) {
        if (routeSkuList.size()==0) {
            return new ArrayList<>();
        }
        // 准备数据 spu
        // 使用completableFuture异步完成下面的任务
        CompletableFuture<Map<Long, RouteSpu>> routeSpuCF = CompletableFuture.supplyAsync(() -> {
            Set<Long> spuIdList = routeSkuList.stream().map(RouteSku::getRouteSpuId).collect(Collectors.toSet());
            Map<Long, RouteSpu> routeSpuMap =
                routeSpuMapper.selectList(new QueryWrapper<RouteSpu>().in(RouteSpu.COL_ID, spuIdList)).stream()
                    .collect(Collectors.toMap(RouteSpu::getId, routeSpu -> routeSpu));
            return routeSpuMap;
        },threadPoolExecutor);
        // destination
        CompletableFuture<Map<Long, RouteDestination>> destinationMapCF = CompletableFuture.supplyAsync(() -> {
            Set<Long> destinationIdList =
                routeSkuList.stream().map(RouteSku::getDestinationId).collect(Collectors.toSet());
            Map<Long, RouteDestination> destinationMap = destinationMapper.selectList(
                    new QueryWrapper<RouteDestination>().in(RouteDestination.COL_ID, destinationIdList)).stream()
                .collect(Collectors.toMap(RouteDestination::getId, destination -> destination));
            return destinationMap;
        },threadPoolExecutor);
        // time
        CompletableFuture<Map<Long, RouteTime>> timeMapCF = CompletableFuture.supplyAsync(() -> {
            Set<Long> timeIdList = routeSkuList.stream().map(RouteSku::getTimeId).collect(Collectors.toSet());
            Map<Long, RouteTime> timeMap = timeMapper.selectList(new QueryWrapper<RouteTime>().in(RouteTime.COL_ID, timeIdList))
                .stream().collect(Collectors.toMap(RouteTime::getId, time -> time));
            return timeMap;
        },threadPoolExecutor);
        // seat
        CompletableFuture<Map<Long, RouteSeat>> seatMapCF = CompletableFuture.supplyAsync(() -> {
            Set<Long> seatIdList = routeSkuList.stream().map(RouteSku::getSeatId).collect(Collectors.toSet());
            Map<Long, RouteSeat> seatMap =
                seatMapper.selectList(new QueryWrapper<RouteSeat>().in(RouteSeat.COL_ID, seatIdList)).stream()
                    .collect(Collectors.toMap(RouteSeat::getId, seat -> seat));
            return seatMap;
        },threadPoolExecutor);
        CompletableFuture.allOf(routeSpuCF, destinationMapCF, timeMapCF, seatMapCF).join();
        // 组装数据
        List<RouteSkuQueryVo> resultList = new ArrayList<>();
        routeSkuList.forEach(routeSku -> {
            RouteSkuQueryVo routeSkuQueryVo = new RouteSkuQueryVo();
            routeSkuQueryVo.setSkuId(routeSku.getId());
            routeSkuQueryVo.setRouteSpu(routeSpuCF.join().get(routeSku.getRouteSpuId()));
            routeSkuQueryVo.setDestination(destinationMapCF.join().get(routeSku.getDestinationId()).getDestination());
            routeSkuQueryVo.setFishList(destinationMapCF.join().get(routeSku.getDestinationId()).getFishList());
            routeSkuQueryVo.setStartTime(timeMapCF.join().get(routeSku.getTimeId()).getStartTime());
            routeSkuQueryVo.setEndTime(timeMapCF.join().get(routeSku.getTimeId()).getEndTime());
            if (routeSku.getSeatId() != null) {
                routeSkuQueryVo.setSeatId(routeSku.getSeatId());
                routeSkuQueryVo.setSeatNo(seatMapCF.join().get(routeSku.getSeatId()).getSeatNo());
            }
            routeSkuQueryVo.setAmount(routeSku.getAmount());
            routeSkuQueryVo.setStock(routeSku.getStock());
            resultList.add(routeSkuQueryVo);
        });
        return resultList;
    }

    @Override public boolean checkBeyondDeadline(Long skuId) {
        RouteSku routeInfo = routeSkuMapper.selectById(skuId);
        Optional.ofNullable(routeInfo).orElseThrow(() -> new CustomException(ExceptionEnum.ROUTE_NOT_FOUND));
        RouteSpu routeSpu = routeSpuMapper.selectById(routeInfo.getRouteSpuId());
        Optional.ofNullable(routeSpu).orElseThrow(() -> new CustomException(ExceptionEnum.ROUTE_NOT_FOUND));
        return routeSpu.getDeadline().getTime() < System.currentTimeMillis();
    }
}
